# cmake的命令是不区字母分大小写的

# 设置项目所需的cmake的最低版本
cmake_minimum_required(VERSION 3.5)

# 设置项目名， 项目使用的编程语言, 项目的版本
# 本命令储存项目名至PROJECT_NAME和CMAKE_PROJECT_NAME
# 本命令会修改PROJECT_BINARY_DIR和PROJECT_SOURCR_DIR的值， 多次调用多次修改
# 如果有多次project，储存最后一次的值
project(learncmake LANGUAGES CXX VERSION 0.1)

# message()在执行cmake时打印信息
message("PROJECT_NAME = ${PROJECT_NAME}")  # PROJECT_NAME是最近一次CMakeLists.txt里的project()定义的项目名， 不区分目录
message("CMAKE_PROJECT_NAME = ${CMAKE_PROJECT_NAME}")  # CMAKE_PROJECT_NAME是顶级目录内CMakeLists.txt里的project()定义的项目名

option(USE_LIBDEMO "Use libDemo example" ON)

# 控制静态库和共享库的构建位置，以便在windows上不需要修改运行可执行文件的路径
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")

# BUILD_SHARED_LIBS是cmake本身自带的变量, 如果add_library()没有显式指定生成库的类型，
# 它就会读取BUILD_SHARED_LIBS的值进行判断(ON:share, off:static)
option(BUILD_SHARED_LIBS "Build using shared libraries" ON) # 选择生成静态库还是共享库


# 将输入文件替换引用为@VAR@或${VAR}的变量值后输出文件, 输出文件会放置到PROJECT_BINARY_DIR项目二进制目录
configure_file(CfgFileDemo.h.in CfgFileDemo.h)

# 设置c++11标准
set(CMAKE_CXX_STANDARD 11)
# CMAKE_CXX_STANDARD设置的标准版本是否强制要求
set(CMAKE_CXX_STANDARD_REQUIRED ON)

#  编译信息
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 以json文件保存编译命令行, 生成的compile_commands.json 在构建目录，仅makefile和ninja构建器可用
set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "" FORCE)  # 强制显示makefile信息

# 生成器表达式示例
message("CMAKE_C_COMPILER_ID = ${CMAKE_C_COMPILER_ID}")
message("CMAKE_C_COMPILER_VERSION = ${CMAKE_C_COMPILER_VERSION}")
message("CMAKE_CXX_COMPILER_ID = ${CMAKE_CXX_COMPILER_ID}")
message("CMAKE_CXX_COMPILER_VERSION = ${CMAKE_CXX_COMPILER_VERSION}")
#message("$<CONFIG:Debug>") # 生成器表达式是构建时求值，所以message不可用
add_custom_target(
    learncmake_compiler_flags ALL
    COMMAND ${CMAKE_COMMAND} -E echo $<$<CONFIG:Debug>:debug>  # 查看生成器表达式值方法， 也可使用 file(GENERATE OUTPUT ...)
    COMMAND ${CMAKE_COMMAND} -E echo "$<VERSION_GREATER:19.16.27045.0, 18.99>"  #18.99.99999.9
    )
# 其它设定编译选项的用法法
#target_compile_options() #/wd"4819" /utf-8
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0")
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2")
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
#add_compile_options("$<$<CONFIG:Debug>:-g;-O0>")
#add_compile_options($<$<CONFIG:Release>:-O2>)
# 可以在 `cmake --build xxx --target xxx`之后加上 `-v`，转递给ninja，显示详细的编译命令行
add_compile_options("$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<VERSION_GREATER:$<CXX_COMPILER_VERSION>,18.99>>:/wd4819;/UTF-8>")

# 添加子项目
message("USE_LIBDEMO = ${USE_LIBDEMO}")
if(USE_LIBDEMO)
    add_subdirectory(LibDemo)
    list(APPEND EXTRA_LIBS libDemo) # 这里的libDemo是指 add_library指定的target，不是文件
#    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/LibDemo")
#    list(APPEND EXTRA_INCLUDES libDemo) # 这里的libDemo是指 add_library指定的target，不是文件
    message("EXTRA_LIBS = ${EXTRA_LIBS}")
#    message("EXTRA_INCLUDES = ${EXTRA_INCLUDES}")
    get_target_property(VGOT libDemo INTERFACE_INCLUDE_DIRECTORIES)  # INTERFACE_INCLUDE_DIRECTORIES
    message("libDemo.INTERFACE_INCLUDE_DIRECTORIES = ${VGOT}")

endif()

# 添加fmt库
set(BUILD_SHARED_LIBS_OLD ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF) #fmt默认使用BUILD_SHARED_LIBS作共享生成标记，先设置为OFF，添加fmt后再恢复
add_subdirectory(${PROJECT_SOURCE_DIR}/fmt)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_OLD})
#get_target_property(VGOT fmt SOURCES)
#message("fmt.SOURCES = ${VGOT}")




# 添加一个名为learncmake的可执行文件生成， 并设定生成文件需要的源代码
add_executable(learncmake main.cpp)

target_link_libraries(learncmake PUBLIC ${EXTRA_LIBS})

message("PROJECT_BINARY_DIR = ${PROJECT_BINARY_DIR}")

# 添加fmt库链接信息, 双冒号::是库的别名，一般在cmake避免与文件名混淆
target_link_libraries(learncmake PRIVATE fmt::fmt)

# 添加OpenCV库
# 添加OpenCV库链接信息
include(OpenCV.cmake)
target_link_libraries(learncmake PRIVATE OpenCV::OpenCV)
target_include_directories(learncmake PUBLIC OpenCV::OpenCV)

# 设定编译目标文件时要使用的包含目录。目标必须已经由add_executable() or add_library() 指定
# 公有私有的用法，可参考这https://zhuanlan.zhihu.com/p/82244559
target_include_directories(learncmake PUBLIC
    "${PROJECT_BINARY_DIR}"
#    ${EXTRA_INCLUDES}  # 这里的是指 add_library指定的target，不是文件
    )

get_target_property(VGOT learncmake INCLUDE_DIRECTORIES)
message("learncmake.INCLUDE_DIRECTORIES = ${VGOT}")
get_target_property(VGOT learncmake INTERFACE_INCLUDE_DIRECTORIES)
message("learncmake.INTERFACE_INCLUDE_DIRECTORIES = ${VGOT}")

# 安装示例
set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/Install/") # 修改默认安装位置，不然它默认在c盘
install(TARGETS learncmake DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/CfgFileDemo.h" DESTINATION bin)

# cmake自带一些测试，测试的使用
enable_testing() # 使能测试功能
add_test(NAME Runs COMMAND learncmake "") # 添加一个命令为 learncmake.exe "" 的测试，测试名为Runs
set_tests_properties(Runs PROPERTIES PASS_REGULAR_EXPRESSION "0")  # 设置测试名为Runs的测试字符串输出是否包含指定的信息


# 配置时自检示例
# 注意这些代码仅被编译检测一次，除非删除CMakeCache.txt对应的项
include(CheckCXXSourceCompiles)  #  CheckCXXSourceCompiles是cmake自带的库，用于检测c/cpp代码能否被编译链接
check_cxx_source_compiles("
  #include <cmath>
  int main() {
    std::log(1.0);
    return 0;
  }
" HAVE_LOG)
check_cxx_source_compiles("
  #include <cmath>
  int main() {
    std::exp(1.0);
    return 0;
  }
" HAVE_EXP)
if(HAVE_LOG AND HAVE_EXP)
  target_compile_definitions(libDemo
                             PRIVATE "HAVE_LOG" "HAVE_EXP")
  message("HAVE_LOG=${HAVE_LOG}, HAVE_EXP=${HAVE_EXP}")
endif()

# 用户自定义命令示例
#add_dependencies(learncmake print-custom-txt)
add_custom_command(
    TARGET learncmake
    POST_BUILD # 执行时机
    COMMAND learncmake
    )

# 生成打包安装器
# 打包依赖于CMAKE_INSTALL_PREFIX
# 生成打包，要在安装目前运行cpack.exe, 例如cpack -G ZIP -C Debug
include(InstallRequiredSystemLibraries)

#set(CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}/Install")
message("CPACK_PACKAGE_DIRECTORY = ${CPACK_PACKAGE_DIRECTORY}")

#set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${learncmake_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${learncmake_VERSION_MINOR}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)
